#include <menu.h>  // This also includes the curses.h file
#include <stdlib.h>
#include <stdio.h>
#include <aio.h>
#include <sys/time.h>
#include <ctype.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "constants_genral.h"
#include "otherCars.h"
#include "highscore_easy.h"
#include "highscore_medium.h"
#include "highscore_hard.h"
#include "timer.h"
#include "print_file.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define CTRLD   4

int     cur;
int 	game(int Speed);
void	on_input();
void	setup_aio_buffer();


/*Menu Items*/
char *choices[] = {
                        "Easy Game",
                        "Medium Game",
                        "Hard Game",
                        "Score Screen",
                        "How To Play",
                        "Exit",
                        (char *)NULL,
                  };

struct aiocb kbcbuf;

int strow;
int stcol;
int quit = 0;

int main()
{      
        int menu_select,
                row,
                col,
                c,
                n_choices,
                i,
		win = 0;
	

        ITEM	 **my_items;                                
        MENU	 *my_menu;
    	WINDOW	*my_menu_win;
        
        /* Initialize curses */
        initscr();
    	cbreak();
    	noecho();
        keypad(stdscr, TRUE);

	getmaxyx(stdscr,row,col);

	/*Check users window/terminal size*/
	while(row<ROW || col<COL)
	{
		getmaxyx(stdscr,row,col);
		mvprintw(1,1, "Please resize your terminal to be at least 100x30");
		refresh();
	}
	clear();
	row=ROW;
	col=COL;

	intro_car();	/* Run title screen */
//	sleep(3);

        /* Create items */
        n_choices = ARRAY_SIZE(choices);
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));
        for(i = 0; i < n_choices; ++i)
                my_items[i] = new_item(choices[i], NULL);

        /* Create menu */
                my_menu = new_menu((ITEM **)my_items);

        /* Create the window to be associated with the menu */
       my_menu_win = newwin(10, 40, 4, 4);
     
        /* Set main window and sub window */
        set_menu_win(my_menu, my_menu_win);
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));

        /* Set menu mark to the string " * " */
        set_menu_mark(my_menu, " -- ");

        /* Print a border around the main window and print a title */
        box(my_menu_win, 0, 0);

        

        refresh();
        
        /* Post the menu */
        post_menu(my_menu);
        wrefresh(my_menu_win);

        while(((c = getch()) != 'q') && !win)
        {       switch(c)
                {       
                	case KEY_DOWN:   
                                menu_driver(my_menu, REQ_DOWN_ITEM);
                                break;
                        case KEY_UP:
                                menu_driver(my_menu, REQ_UP_ITEM);
                                break;
                        case 10: /* Enter */
                        {       
                                ITEM *cur;
                                //void (*p)(char *);
                                cur = current_item(my_menu);
                                //p = item_userptr(cur);
                                menu_select = item_index(cur);
                                
                                if(menu_select == 0)
				{
					print_intro();
					Difficulty = EASY;
					sec = 0;
					score = 0;
					Level = 1;
					if(game(SPEED_LV1)) break;
					Level = 2;
					score = score + 1000;
					if(game(SPEED_LV2)) break;
					Level = 3;
					score = score + 1000;
					if(game(SPEED_LV3)) break;
					Level = 4;
					score = score + 1000;
					if(game(SPEED_LV4)) break;
					Level = 5;
					score = score + 1000;
					if(game(SPEED_LV5)) break;
					if(Level == 5)
					{
						score = score + 1000;
						highscore_easy();
						win = 1;
						clear();
						mvprintw(5, 20, "CONGRATULATIONS! You made it to work without spilling your coffee.");
						refresh();
						sleep(4);
					}
					clear();
					refresh();
        				box(my_menu_win, 0, 0);
                			wrefresh(my_menu_win);
				}
                                if(menu_select == 1)
                                {
					print_intro();
					Difficulty = MEDIUM;
					sec = 0;
					score = 0;
					Level = 1;
					if(game(SPEED_LV1)) break;
					Level = 2;
					score = score + 1500;
					if(game(SPEED_LV2)) break;
					Level = 3;
					score = score + 1500;
					if(game(SPEED_LV3)) break;
					Level = 4;
					score = score + 1500;
					if(game(SPEED_LV4)) break;
					Level = 5;
					score = score + 1500;
					if(game(SPEED_LV5)) break;
					if(Level == 5)
					{
						score = score + 1500;
						highscore_medium();
						win = 1;
						clear();
						mvprintw(5, 20, "CONGRATULATIONS! You made it to work without spilling your coffee.");
						refresh();
						sleep(4);
					}
					clear();
					refresh();
        				box(my_menu_win, 0, 0);
                			wrefresh(my_menu_win);
				}
								
                                if(menu_select == 2)
                                {
					print_intro();
					Difficulty = HARD;
					sec = 0;
					score = 0;
					Level = 1;
					if(game(SPEED_LV1)) break;
					Level = 2;
					score = score + 2000;
					if(game(SPEED_LV2)) break;
					Level = 3;
					score = score + 2000;
					if(game(SPEED_LV3)) break;
					Level = 4;
					score = score + 2000;
					if(game(SPEED_LV4)) break;
					Level = 5;
					score = score + 2000;
					if(game(SPEED_LV5)) break;
					if(Level == 5)
					{
						score = score + 2000;
						highscore_hard();
						win = 1;
						clear();
						mvprintw(5, 20, "CONGRATULATIONS! You made it to work without spilling your coffee.");
						refresh();
						sleep(4);
					}
					clear();
					refresh();
        				box(my_menu_win, 0, 0);
                			wrefresh(my_menu_win);
				}
				if(menu_select == 3)
				{
					print_scores();
					clear();
					refresh();
        				box(my_menu_win, 0, 0);
                			wrefresh(my_menu_win);
				}	
				if(menu_select == 4)
				{
					print_howTo();
					clear();
					refresh();
				}
                                if(menu_select == 5)
				{
					clear();
					mvprintw(5,5, "Thank you for playing");
					refresh();
					sleep(4);

				        /* Unpost and free all the memory taken up */
		
			        	unpost_menu(my_menu);
			        	free_menu(my_menu);
			        	for(i = 0; i < n_choices; ++i)
			        	        free_item(my_items[i]);
				        	endwin();
					
					return 0;
				}
                                
                                //pos_menu_cursor(my_menu);
                                break;
                        }
                }
		clear();
		menu_screen();
        	box(my_menu_win, 0, 0);
                wrefresh(my_menu_win);
        }       

        /* Unpost and free all the memory taken up */
        unpost_menu(my_menu);
        free_menu(my_menu);
        for(i = 0; i < n_choices; ++i)
                free_item(my_items[i]);
        endwin();

	return 0;
}

int game(int Speed)
{
        struct	itimerval value;

	int     row,
		col,
		carRow = 0,       /* carRow and laneRow store the rows the functions last stoped printing on */
		laneRow = 0,
		end = 0,
		quit = 0;
	
			
        //crmode();
        clear();
	//getmaxyx(stdscr,row,col);
	/*set the wrapper for the game*/
	row = ROW;
	col = COL;

        timer_win = newwin(row, 22, 0, col - 22);	/* Status bar on right side */

	strow = row - 3;
	stcol = (col/LANESTART) +(Difficulty +2)*LANEWIDTH+3;
	
	signal(SIGIO, on_input);

        setup_aio_buffer();
        aio_read(&kbcbuf);

	start_color();			/* Start color 			*/
	init_pair(1, COLOR_RED, COLOR_BLACK);
	init_pair(2, COLOR_BLUE, COLOR_BLACK);
	init_pair(3, COLOR_GREEN, COLOR_BLACK);
		
        signal(SIGALRM, alarm_handler);                 /* Sets timer to update	*/
        memset(&value, 0, sizeof(value));               /* every second		*/
        value.it_interval.tv_sec = 1;
        value.it_value.tv_sec    = 1;
        setitimer(ITIMER_REAL, &value, NULL); 		/* alarm reset after timer countdown */
        signal(SIGINT, finish); /* Ctrl+C exits program */

	while(!quit && !end)
        {
		switch(Difficulty)	/*Diffuculty goes in switch*/
		{
			case EASY: /*EASY = 0*/
				laneRow = drawLanesEasy(SPEED_LV1, stdscr, laneRow);            /* call draw lanes with speed constant, window pointer, and the row it was last on */
                		carRow = other_carsEasy(Speed , stdscr, carRow);                /* same as above but prints the "other" cars */
                		carRow+=1;      /* move car one space down */
                		laneRow+=2;     /* move lanes down by 3 to allow spaces in the lines */
				break;
			case MEDIUM: /*MEDIUM = 1*/
						laneRow = drawLanesMedium(Speed, stdscr, laneRow);       
                		carRow = other_carsMedium(SPEED_LV1 , stdscr, carRow);            
                		carRow+=1;      
                		laneRow+=3;    
				break;
			case HARD: /*HARD = 2*/
				laneRow = drawLanesHard(Speed, stdscr, laneRow);     
                		carRow = other_carsHard(Speed , stdscr, carRow);        
                		carRow+=1;  
                		laneRow+=3;
				break;
		}
				//mvprintw(1,1, "%d ----- %d", stcol, lanecar);
				//sleep(2);
        	if((strow-1 != carRow))
        	{
                       	mvprintw(strow-2, stcol-LANEWIDTH, "      ");
                       	mvprintw(strow-1, stcol-LANEWIDTH, "      ");
                        mvprintw(strow, stcol-LANEWIDTH, "      ");
                        mvprintw(strow+1, stcol-LANEWIDTH, "      ");
                        mvprintw(strow+2, stcol-LANEWIDTH, "      ");
                                
                        mvprintw(strow-2, stcol+LANEWIDTH, "      ");
                       	mvprintw(strow-1, stcol+LANEWIDTH, "      ");
                       	mvprintw(strow, stcol+LANEWIDTH, "      ");
                        mvprintw(strow+1, stcol+LANEWIDTH, "      ");
                        mvprintw(strow+2, stcol+LANEWIDTH, "      ");
                        
			attron(COLOR_PAIR(3));        
                    	mvprintw(strow-2, stcol, "  __");
                    	mvprintw(strow-1, stcol, " /_ \\ ");
                    	mvprintw(strow, stcol, "(|v |)");
                        mvprintw(strow+1, stcol, " |o |");
                        mvprintw(strow+2, stcol, "(|__|)");
			attroff(COLOR_PAIR(3));
		}
		else if((strow-1 == carRow) && (stcol == (15+(11*(lanecar-1)))))
		{
			switch(Difficulty)
			{
				case 0:	score = score + 50;
					break;
				case 1:	score = score + 100;
					break;
				case 2:	score = score + 150;
			}
                       	mvprintw(strow-2, stcol-LANEWIDTH, "      ");
                       	mvprintw(strow-1, stcol-LANEWIDTH, "      ");
                        mvprintw(strow, stcol-LANEWIDTH, "      ");
                        mvprintw(strow+1, stcol-LANEWIDTH, "      ");
                        mvprintw(strow+2, stcol-LANEWIDTH, "      ");
                                
                        mvprintw(strow-1, stcol+LANEWIDTH, "      ");
                        mvprintw(strow-2, stcol+LANEWIDTH, "      ");
                       	mvprintw(strow-1, stcol+LANEWIDTH, "      ");
                       	mvprintw(strow, stcol+LANEWIDTH, "      ");
                        mvprintw(strow+1, stcol+LANEWIDTH, "      ");
                                
			attron(COLOR_PAIR(3));        
                    	mvprintw(strow-2, stcol, "  __");
                    	mvprintw(strow-1, stcol, " /_ \\ ");
                    	mvprintw(strow, stcol, "(|v |)");
                        mvprintw(strow+1, stcol, " |o |");
                        mvprintw(strow+2, stcol, "(|__|)");
			attroff(COLOR_PAIR(3));
                }
                else if((strow-1 == carRow) && (stcol != (15+(11*(lanecar-1)))))
                {
			flash();
			beep();
                       	sleep(1);
                       	quit=1;
                }
						
		/*  Keeps timer updating */
		box(timer_win, 0, 0);
			
		switch(Difficulty)
		{
			case 0: 
				end = disp_timer_easy();
				break;
			case 1:
				end = disp_timer_medium();
				break;
			case 2:
				end = disp_timer_hard();
				break;
		}
			
                wrefresh(timer_win);

	}

        setitimer(ITIMER_REAL, NULL, NULL);		/* Turns off timer alarm */
	signal(SIGIO, SIG_IGN);
	echo();

	if(end == 1)                   /* If user pushed 'q', enter    */
	{
		sec++;
                wclear(timer_win);
                clear();
                refresh();
                mvprintw(1, 1, "LEVEL UP");
                menu_screen();
                mvprintw(17,50,"Press Enter twice to contnue");
		getch();
                refresh();
	}
        else                            /* Otherwise, game over.        */
        {
       		switch(Difficulty)
        	{
        	        case 0:
        	                highscore_easy();
                        	break;
               		case 1:
                        	highscore_medium();
                        	break;
                	case 2:
                        	highscore_hard();
                        	break;
        	}
		return 1;
        }

	quit = 0;

	noecho();

        return 0;
}
        
        
void on_input()
{
        int  c, row, col;
        char *cp = (char *) kbcbuf.aio_buf;     /* cast to char * */

	getmaxyx(stdscr,row,col);
                
        /* check for errors */
        if ( aio_error(&kbcbuf) != 0 )
                perror("reading failed");
        else
                /* get number of chars read */
                if ( aio_return(&kbcbuf) == 1 )
//(col/LANESTART)+(Difficulty)/LANEWIDTH))
                {
                      c = *cp;
                      if((c == 'a') && (stcol > 15)) stcol-=LANEWIDTH;
                      if((c == 'd') && (stcol < ((col/LANESTART)+(Difficulty+1)*LANEWIDTH))) stcol+=LANEWIDTH;
                      if(c == 'q') quit=1;
                }

        /* place a new request */
        aio_read(&kbcbuf);
	
}


void setup_aio_buffer()
{
        static char input[1];                 /* 1 char of input */

        /* describe what to read */
        kbcbuf.aio_fildes     = 0;            /* standard intput */
        kbcbuf.aio_buf        = input;        /* buffer          */
        kbcbuf.aio_nbytes     = 1;             /* number to read  */
        kbcbuf.aio_offset     = 0;             /* offset in file  */

        /* describe what to do when read is ready */
        kbcbuf.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
        kbcbuf.aio_sigevent.sigev_signo  = SIGIO;  /* send sIGIO   */
}

